;; -------- ESS smart operators (for R) --------
(use-package ess-smart-equals
  :init
  (setq ess-smart-equals-extra-ops '(brace paren percent))
  (defcustom ess-smart-equals-contexts
    '((t (comment)
         (string)
         (arglist "=" "==" "!=" "<=" ">=" "<-" "<<-" "%>%")
         (index "=" "==" "%in%" "!=" "<" "<=" ">" ">=")
         ;; This order of inequalities makes cycling align with completion
         (conditional "==" "!=" "<" "<=" ">" ">=" "%in%")
         ;; base holds all operators that are assignment or complete with '='
         (base "<-" "<<-" "=" "==" "!=" "<=" ">=" "->" "->>" ":=")
         ;; Used for smart %-completion and cycling
         (% "%>%" "%in%" "%%" "%*%" "%/%" "%<>%" "%o%" "%x%")
         ;; Used for removal in ess-smart-equals-percent
         (not-% "<-" "<<-" "=" "->" "->>"
                "==" "!=" "<" "<=" ">" ">="
                "+" "-" "*" "**" "/" "^" "&" "&&" "|" "||")
         ;; All the principal binary operators
         (all "<-" "<<-" "=" "->" "->>"
              "==" "!=" "<" "<=" ">" ">="
              "%*%" "%%" "%/%" "%in%" "%x%" "%o%"
              "%<>%" "%>%" ; not builtin but common (dynamic?)
              "+" "-" "*" "**" "/" "^" "&" "&&" "|" "||")
         (t "<-" "==" "->" "<=" ">=" "=" "%<>%"))
      (ess-roxy-mode
       (comment "<-" "=" "==" "<<-" "->" "->>" "%<>%")))
    "Prioritized lists of operator strings for each context and major mode. ..."
    :group 'ess-edit
    :type '(alist
            :key-type symbol
            :value-type (alist :key-type symbol :value-type (repeat string))))
  :after
  (:any ess-r-mode inferior-ess-r-mode ess-r-transcript-mode)
  :config
  (ess-smart-equals-activate))

;; ;; -------- macros for shortcuts compatible with Rstudio --------
;; ;; p1uxtar-R-assignment-symbol
;; (fset 'p1uxtar-R-assign-symbol
;;       (lambda (&optional arg)
;;         "Keyboard macro."
;;         (interactive "p")
;;         (kmacro-exec-ring-item (quote ("=" 0 "%d")) arg)))
;; ;; p1uxtar-R-pipe-command
;; (fset 'p1uxtar-R-pipe-command
;;       (lambda (&optional arg)
;;         "Keyboard macro."
;;         (interactive "p")
;;         (kmacro-exec-ring-item (quote ("%" 0 "%d")) arg)))

;; -------- update ESS (for R on Linux) --------
(use-package ess-site
  :ensure ess
  :init
  (setq comint-prompt-read-only t
        comint-scroll-to-bottom-on-input 'this
        comint-scroll-to-bottom-on-output 'others
        ess-ask-for-ess-directory nil
        ess-history-file "~/R/.R_history"
        ;; ess-indent-level 2
        ;; ess-set-style 'GNU
        ;; ess-set-style 'RStudio-
        ess-tab-complete-in-script t
        ess-use-eldoc 'script-only
        ess-use-flymake nil
        inferior-R-args "--no-save --quiet")
  ;; highlight R functions etc.
  ;; (setq inferior-R-font-lock-keywords
  ;;       '((ess-S-fl-keyword:prompt . t)
  ;;         (ess-R-fl-keyword:messages . t)
  ;;         (ess-R-fl-keyword:modifiers . t)
  ;;         (ess-R-fl-keyword:fun-defs . t)
  ;;         (ess-R-fl-keyword:keywords . t)
  ;;         (ess-R-fl-keyword:assign-ops . t)
  ;;         (ess-R-fl-keyword:constants . t)
  ;;         (ess-fl-keyword:matrix-labels . t)
  ;;         (ess-fl-keyword:fun-calls . t)
  ;;         (ess-fl-keyword:numbers . t)
  ;;         (ess-fl-keyword:operators . t)
  ;;         (ess-fl-keyword:delimiters . t)
  ;;         (ess-fl-keyword:= . t)
  ;;         (ess-R-fl-keyword:F&T . t)
  ;;         (ess-R-fl-keyword:%op% . t)
  ;;         ))

  ;; -------- highlight function names & keywords in R --------
  (dolist (hook '(ess-mode-hook ess-r-mode-hook inferior-ess-r-mode-hook))
    (add-hook hook
              (lambda()
                (font-lock-add-keywords
                 nil
                 '(("\\<\\(if\\|for\\|function\\|return\\|$\\|@\\)\\>[\n[:blank:]]*(" 1
                    font-lock-keyword-face) ; must go first to override highlighting below
                   ("\\<\\([.A-Za-z][._A-Za-z0-9]*\\)[\n[:blank:]]*(" 1
                    font-lock-function-name-face) ; highlight function names
                   ("\\([(,]\\|[\n[:blank:]]*\\)\\([.A-Za-z][._A-Za-z0-9]*\\)[\n[:blank:]]*=[^=]"
                    2 font-lock-builtin-face)
                   ;; highlight numbers
                   ("\\(-?[0-9]*\\.?[0-9]*[eE]?-?[0-9]+[iL]?\\)" 1 font-lock-constant-face)
                   ;; highlight operators
                   ("\\(\\$\\|\\@\\|\\!\\|\\%\\|\\^\\|\\&\\|\\*\\|\(\\|\)\\|\{\\|\}\\|\\[\\|\\]\\|\\-\\|\\+\\|\=\\|\\/\\|\<\\|\>\\|:\\)" 1 font-lock-type-face)
                   ;; highlight S4 methods
                   ("\\(setMethod\\|setGeneric\\|setGroupGeneric\\|setClass\\|setRefClass\\|setReplaceMethod\\)" 1 font-lock-preprocessor-face)
                   ;; highlight packages called through ::, :::
                   ("\\(\\w+\\):\\{2,3\\}" 1 font-lock-type-face)
                   )))))

  ;; (autoload 'ess-r-mode "ess-site.el" nil t)
  (eval-after-load "comint"
    '(progn
       (define-key comint-mode-map "\C-p"
         'comint-previous-matching-input-from-input)
       (define-key comint-mode-map "\C-n"
         'comint-next-matching-input-from-input)
       (define-key comint-mode-map "\C-u"
         'comint-kill-input)))

  ;; (use-package lsp-mode
  ;;   :commands lsp
  ;;   :config
  ;;   (lsp-register-client
  ;;    (make-lsp-client :new-connection
  ;; 			(lsp-stdio-connection '("R" "--slave" "-e" "languageserver::run()"))
  ;; 			:major-modes '(ess-r-mode inferior-ess-r-mode)
  ;; 			:server-id 'lsp-R)))

  (use-package eglot
    :config
    (add-to-list 'eglot-server-programs
                 '((ess-r-mode inferior-ess-r-mode)
                   . ("R" "--slave" "-e" "languageserver::run()"))))

  ;; func to run R
  (defun p1uxtar-run-r ()
    (interactive)
    (cond
     ((string-equal system-type "gnu/linux")
      (setq ess-startup-directory "~/R"))
     ((string= (system-name) "RZ-ZZ-PC-PG004")
      (setq ess-startup-directory "c:/Users/Yuanchen.Xie/Documents/R")
      (setq inferior-R-program-name "c:/Program Files/R/R-4.0.3/bin/R.exe")))
    (run-ess-r))

  ;; package by shuguang79
  (use-package ess-view-data
    :defer t
    :init
    (setq ess-view-data-current-backend 'data.table+magrittr)
    :bind
    (("C-c v" . 'ess-view-data-print)))
  ;; insert $ with tidyverse
  (use-package ess-r-insert-obj
    :ensure t)

  :commands R
  :bind
  (("C-c r" . #'p1uxtar-run-r)
   ;; ("C-c R" . #'p1uxtar-run-raw-r)
   ;; ("C-S-m" . 'p1uxtar-R-pipe-command)
   ("C-c m r" . 'R-mode)
   ;; ("M--" . 'p1uxtar-R-assign-symbol)
   ;; :map comint-mode-map
   ;; ("C-c e" . 'ess-rdired)
   )
  :hook
  ((inferior-ess-r-mode . eldoc-mode)
   (ess-mode . (lambda ()
                 (company-mode)
                 (electric-pair-mode 1))))
  :diminish eldoc-mode
  :config
  ;; company with tabnine & R
  ;; via: github.com/emacs-ess/ESS/issues/955
  (setq ess-r-company-backends
        '((company-tabnine
           company-R-library
           company-R-args
           company-R-objects
           company-dabbrev-code
           :separate))))

;; -------- poly-mode --------
(use-package polymode
  :mode (("*\\.Rmd" . Rmd-mode))
  :init
  (defun Rmd-mode ()
    "ESS Markdown mode for Rmd files"
    (interactive)
    (require 'poly-R)
    (require 'poly-markdown)
    (R-mode)
    (poly-markdown+r-mode))
  :bind
  (("C-c m d" . 'poly-markdown+r-mode)))


;; -------- display *R* buffer --------
;; (setq display-buffer-alist
;;       `(("*R"
;;          (display-buffer-reuse-window display-buffer-in-side-window)
;;          (side . right)
;;          (slot . -1)
;;          (window-width . 0.5)
;;          (reusable-frames . nil))
;;         ;; ("*Help"
;;         ;;  (display-buffer-reuse-window display-buffer-at-bottom)
;;         ;;  (window-width . 0.5)
;;         ;;  (reusable-frames . nil))
;;         ("\\.R$"
;;          (display-buffer-reuse-window display-buffer-in-side-window)
;;          (side . left)
;;          (slot . 1)
;;          (window-width . 0.5)
;;          (reusable-frames . nil))))

;; -------- three functions to format R code --------
;;   (defun p1uxtar-ess-indent-region-as-r (beg end)
;;     "Format region of code R using the R parser."
;;     (interactive "r")
;;     (let ((string (replace-regexp-in-string
;; 		   "\"" "\\\\\\&"
;; 		   (replace-regexp-in-string ;; how to avoid this double matching?
;; 		    "\\\\\"" "\\\\\\&" (buffer-substring-no-properties beg end))))
;; 	  (buf (get-buffer-create "*ess-command-output*")))
;;       (ess-force-buffer-current "Process to load into:")
;;       (ess-command
;;        (format
;; 	"local({oo <- options(keep.source = FALSE);
;; cat('\n', paste(deparse(parse(text = \"%s\")[[1L]]), collapse = '\n'), '\n', sep = '')
;; options(oo)})\n"
;; 	string) buf)
;;       (with-current-buffer buf
;; 	(goto-char (point-max))
;; 	;; (skip-chars-backward "\n")
;; 	(let ((end (point)))
;; 	  (goto-char (point-min))
;; 	  (goto-char (1+ (point-at-eol)))
;; 	  (setq string (buffer-substring-no-properties (point) end))
;; 	  ))
;;       (delete-region beg end)
;;       (insert string)
;;       ))

;; (defun p1uxtar-ess-indent-region-with-formatR (beg end)
;;   "Format region of code R using formatR::tidy_source()."
;;   (interactive "r")
;;   (let ((string
;; 	   (replace-regexp-in-string
;; 	    "\"" "\\\\\\&"
;; 	    (replace-regexp-in-string ;; how to avoid this double matching?
;; 	     "\\\\\"" "\\\\\\&"
;; 	     (buffer-substring-no-properties beg end))))
;; 	  (buf (get-buffer-create "*ess-command-output*")))
;;     (ess-force-buffer-current "Process to load into:")
;;     (ess-command
;;      (format                            ; R parser use 'width.cutoff = 60L'
;; 	"local({formatR::tidy_source(text = \"\n%s\", arrow = TRUE, width.cutoff = 60L) })\n"
;; 	string) buf)
;;     (with-current-buffer buf
;; 	(goto-char (point-max))
;; 	;; (skip-chars-backward "\n")
;; 	(let ((end (point)))
;; 	  (goto-char (point-min))
;; 	  (goto-char (1+ (point-at-eol)))
;; 	  (setq string (buffer-substring-no-properties (point) end))
;; 	  ))
;;     (delete-region beg end)
;;     (insert string)
;;     (delete-char -1)
;;     ))

;; (defun p1uxtar-ess-indent-region-with-styler (beg end)
;;   "Format region of code R using styler::style_text()."
;;   (interactive "r")
;;   (let ((string
;; 	   (replace-regexp-in-string
;; 	    "\"" "\\\\\\&"
;; 	    (replace-regexp-in-string ;; how to avoid this double matching?
;; 	     "\\\\\"" "\\\\\\&"
;; 	     (buffer-substring-no-properties beg end))))
;; 	  (buf (get-buffer-create "*ess-command-output*")))
;;     (ess-force-buffer-current "Process to load into:")
;;     (ess-command
;;      (format
;; 	"local({options(styler.colored_print.vertical = FALSE);styler::style_text(text = \"\n%s\", reindention = styler::specify_reindention(regex_pattern = \"###\", indention = 0), indent_by = 4)})\n"
;; 	string) buf)
;;     (with-current-buffer buf
;; 	(goto-char (point-max))
;; 	;; (skip-chars-backward "\n")
;; 	(let ((end (point)))
;; 	  (goto-char (point-min))
;; 	  (goto-char (1+ (point-at-eol)))
;; 	  (setq string (buffer-substring-no-properties (point) end))
;; 	  ))
;;     (delete-region beg end)
;;     (insert string)
;;     (delete-char -1)
;;     ))


(provide '+ESS-R)
